﻿using NCS.IConnect.Core.EntLib.Properties;
using NCS.IConnect.Core.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;

namespace NCS.IConnect.Core.IoC.TransientCall
{
    /// <summary>
    /// Dynamic proxy based service proxy factory.
    /// </summary>
    [MapTo(typeof(IServiceProxyFactory),Lifetime = Lifetime.Singleton)]
    public class DynamicProxyFactory: IServiceProxyFactory
    {
        #region Fields
        private static readonly IDictionary<Type, bool> validationCache = new Dictionary<Type, bool>();
        private static readonly object syncObj = new object();
        #endregion

        #region Public Methods
        /// <summary>
        /// Gets the service proxy.
        /// </summary>
        /// <typeparam name="TProxy">The type of the proxy.</typeparam>
        /// <param name="endpointName">Name of the endpoint.</param>
        /// <returns></returns>
        public TProxy GetServiceProxy<TProxy>(string endpointName)
        {
            Guard.ArgumentNotNullOrEmpty(endpointName, "endpointName");
            EnsureIsServiceContract(typeof(TProxy));
            DefaultChannelProvider channelProvider = new DefaultChannelProvider(typeof(TProxy), endpointName);
            return (TProxy)TransientCallIntercept.GetProxy(typeof(TProxy), channelProvider, new[] { new ChannelExceptionHandlingBehavior() });
        }
        #endregion

        #region Private Methods
        private static void EnsureIsServiceContract(Type contractType)
        {
            var contractIsValid = false;
            if (!validationCache.ContainsKey(contractType))
            {
                lock (syncObj)
                {
                    if(!validationCache.ContainsKey(contractType))
                    {
                        contractIsValid = (contractType.IsInterface && contractType.GetCustomAttributes(typeof(ServiceContractAttribute), false).Length != 0);
                        validationCache[contractType] = contractIsValid;
                    }
                }
            }
            else
            {
                contractIsValid = validationCache[contractType];
            }

            if (!contractIsValid)
            {
                throw new ArgumentException(Resources.ExceptionInvalidServiceContractType.Format((object)contractType.FullName));
            }
        }
        #endregion
    }
}
